package ftpserver

import (
	"crypto/tls"
	"errors"
	"fmt"
	//"math/rand"
	"net"
	"strings"
	"sync"
	"time"

	"gitee.com/xxp2014/logdot"
)

// Active/Passive transfer connection handler
type transferHandler interface {
	// Get the connection to transfer data on
	Open() (net.Conn, error)

	// Close the connection (and any associated resource)
	Close() error

	// Set info about the transfer to return in STAT response
	SetInfo(string)
	// Info about the transfer to return in STAT response
	GetInfo() string
	//客户端连接重新计算
	ClientRecount(num int)
}

// Passive connection
type passiveTransferHandler struct {
	listener    net.Listener     // TCP or SSL Listener
	tcpListener *net.TCPListener // TCP Listener (only keeping it to define a deadline during the accept)
	LisBox      *GlobalTCPListenerBox
	Port        int            // TCP Port we are listening on
	connection  net.Conn       // TCP Connection established
	settings    *Settings      // Settings
	info        string         // transfer info
	logger      *logdot.Logdot // Logger
	// data connection requirement checker
	checkDataConn func(dataConnIP net.IP, channelType DataChannel) error
}

type ipValidationError struct {
	error string
}

func (e *ipValidationError) Error() string {
	return e.error
}

func (c *clientHandler) getCurrentIP() ([]string, error) {
	// Provide our external IP address so the ftp client can connect back to us
	ip := c.server.settings.PublicHost

	// If we don't have an IP address, we can take the one that was used for the current connection
	if ip == "" {
		// Defer to the user-provided resolver.
		if c.server.settings.PublicIPResolver != nil {
			var err error
			ip, err = c.server.settings.PublicIPResolver(c)

			if err != nil {
				return nil, fmt.Errorf("couldn't fetch public IP: %w", err)
			}
		} else {
			ip = strings.Split(c.conn.LocalAddr().String(), ":")[0]
		}
	}

	quads := strings.Split(ip, ".")
	if len(quads) != 4 {
		c.logger.Warn("Invalid passive IP", "IP", ip)

		return nil, &ipValidationError{error: fmt.Sprintf("invalid passive IP %#v", ip)}
	}

	return quads, nil
}

// ErrNoAvailableListeningPort is returned when no port could be found to accept incoming connection
var ErrNoAvailableListeningPort = errors.New("could not find any port to listen to or MaxClientsSingle limit")

var GlobalTCPListener *net.TCPListener

type GlobalTCPListenerBox struct {
	Listener         *net.TCPListener
	ClientNum        int  //传输客户端连接数
	MaxClientsSingle int  //允许最大的客户端连接数量
	IsListened       bool //是否已经启用监听

}

// key:string value:GlobalTCPListenerBox
var GlobalTCPListeners sync.Map

func (c *clientHandler) findListenerWithinPortRange(portRange *PortRange) (*GlobalTCPListenerBox, error) {
	nbAttempts := portRange.End - portRange.Start

	nbAttempts = 1 + nbAttempts
	for i := 0; i < nbAttempts; i++ {
		port := portRange.Start + i
		laddr, errResolve := net.ResolveTCPAddr("tcp", fmt.Sprintf("0.0.0.0:%d", port))
		if errResolve != nil {
			c.logger.Error("Problem resolving local port", "err", errResolve, "port", port)
			continue
			//return nil, fmt.Errorf("could not resolve port %d: %w", port, errResolve)
		}
		_, ok := GlobalTCPListeners.Load(laddr.String())
		if !ok {
			//如果不存在则新监听
			tcpListener, errListen := net.ListenTCP("tcp", laddr)
			if errListen == nil { //监听可能已经存在了,关闭后重新打开
				tcpListener.Close()
				tcpListener, errListen = net.ListenTCP("tcp", laddr)
				if errListen != nil { //监听失败
					c.logger.Error("Problem resolving local port", "err", errListen, "port", port)
					return nil, errListen
				}
			}
			//GlobalTCPListener = tcpListener
			gtb := &GlobalTCPListenerBox{
				Listener:         tcpListener,
				IsListened:       true,
				MaxClientsSingle: c.server.settings.MaxClientsSingle,
				ClientNum:        0,
			}
			GlobalTCPListeners.Store(laddr.String(), gtb)
			return gtb, nil
		}
	}

	var gtlbMinBox *GlobalTCPListenerBox
	//所有端口都已经监听 则遍历出 连接数最少的端口的监听者返回
	GlobalTCPListeners.Range(func(key, value any) bool {
		lis := value.(*GlobalTCPListenerBox)
		if (gtlbMinBox == nil || gtlbMinBox.ClientNum == 0 || gtlbMinBox.ClientNum > lis.ClientNum) && lis.ClientNum < lis.MaxClientsSingle {
			gtlbMinBox = lis
		}
		return true
	})
	if gtlbMinBox == nil {
		c.logger.Warn(
			"Could not find any free port",
			"nbAttempts", nbAttempts,
			"portRangeStart", portRange.Start,
			"portRAngeEnd", portRange.End,
		)
		return nil, ErrNoAvailableListeningPort
	}
	return gtlbMinBox, nil

}

func (c *clientHandler) handlePASV(param string) error {
	command := c.GetLastCommand()
	addr, _ := net.ResolveTCPAddr("tcp", ":0")

	var tcpListener *net.TCPListener
	var tcpListenerBox *GlobalTCPListenerBox
	var err error

	portRange := c.server.settings.PassiveTransferPortRange

	if portRange != nil {
		tcpListenerBox, err = c.findListenerWithinPortRange(portRange)
	} else {
		tcpListener, err = net.ListenTCP("tcp", addr)
	}

	if err != nil {
		c.logger.Error("Could not listen for passive connection", "err", err)
		c.writeMessage(StatusServiceNotAvailable, fmt.Sprintf("Could not listen for passive connection: %v", err))

		return nil
	}
	tcpListener = tcpListenerBox.Listener
	// The listener will either be plain TCP or TLS
	var listener net.Listener

	listener = tcpListener

	if wrapper, ok := c.server.driver.(MainDriverExtensionPassiveWrapper); ok {
		listener, err = wrapper.WrapPassiveListener(listener)
		if err != nil {
			c.logger.Error("Could not wrap passive connection", "err", err)
			c.writeMessage(StatusServiceNotAvailable, fmt.Sprintf("Could not listen for passive connection: %v", err))

			return nil
		}
	}

	if c.HasTLSForTransfers() || c.server.settings.TLSRequired == ImplicitEncryption {
		if tlsConfig, err := c.server.driver.GetTLSConfig(); err == nil {
			listener = tls.NewListener(listener, tlsConfig)
		} else {
			c.writeMessage(StatusServiceNotAvailable, fmt.Sprintf("Cannot get a TLS config: %v", err))

			return nil
		}
	}

	p := &passiveTransferHandler{
		tcpListener: tcpListener,
		listener:    listener,
		Port:        tcpListener.Addr().(*net.TCPAddr).Port,
		settings:    c.server.settings,
		logger: logdot.Create(logdot.Option{
			Dir:          ProgramDir + "runtime/",
			File:         "log/2006-01-02/2006-01-02 15.log",
			SpecificFile: nil,
			Stdout:       true,
		}),
		checkDataConn: c.checkDataConnectionRequirement,
		LisBox:        tcpListenerBox,
	}

	// We should rewrite this part
	if command == "PASV" {
		p1 := p.Port / 256
		p2 := p.Port - (p1 * 256)
		quads, err2 := c.getCurrentIP()

		if err2 != nil {
			c.writeMessage(StatusServiceNotAvailable, fmt.Sprintf("Could not listen for passive connection: %v", err2))

			return nil
		}

		c.writeMessage(
			StatusEnteringPASV,
			fmt.Sprintf("Entering Passive Mode (%s,%s,%s,%s,%d,%d)", quads[0], quads[1], quads[2], quads[3], p1, p2))
	} else {
		c.writeMessage(StatusEnteringEPSV, fmt.Sprintf("Entering Extended Passive Mode (|||%d|)", p.Port))
	}

	c.transferMu.Lock()
	c.transfer = p
	c.transferMu.Unlock()
	c.setLastDataChannel(DataChannelPassive)

	return nil
}

func (p *passiveTransferHandler) ConnectionWait(wait time.Duration) (net.Conn, error) {
	if p.connection == nil {
		var err error
		if err = p.tcpListener.SetDeadline(time.Now().Add(wait)); err != nil {
			return nil, fmt.Errorf("failed to set deadline: %w", err)
		}

		p.connection, err = p.listener.Accept()

		if err != nil {
			return nil, err
		}
		ip, err := getIPFromRemoteAddr(p.connection.RemoteAddr())
		if err != nil {
			p.logger.Warn("Could get remote passive IP address", "err", err)

			return nil, err
		}

		if err := p.checkDataConn(ip, DataChannelPassive); err != nil {
			// we don't want to expose the full error to the client, we just log it
			p.logger.Warn("Could not validate passive data connection requirement", "err", err)

			return nil, &ipValidationError{error: "data connection security requirements not met"}
		}
	}

	return p.connection, nil
}

func (p *passiveTransferHandler) GetInfo() string {
	return p.info
}

func (p *passiveTransferHandler) SetInfo(info string) {
	p.info = info
}

func (p *passiveTransferHandler) Open() (net.Conn, error) {
	timeout := time.Duration(time.Second.Nanoseconds() * int64(p.settings.ConnectionTimeout))

	return p.ConnectionWait(timeout)
}

// Closing only the client connection is not supported at that time
func (p *passiveTransferHandler) Close() error {
	//if p.tcpListener != nil {
	//	if err := p.tcpListener.Close(); err != nil {
	//		p.logger.Warn(
	//			"Problem closing passive listener",
	//			"err", err,
	//		)
	//	}
	//}

	if p.connection != nil {
		if err := p.connection.Close(); err != nil {
			p.logger.Warn(
				"Problem closing passive connection",
				"err", err,
			)
		}

	}
	return nil
}

func (p *passiveTransferHandler) ClientRecount(num int) {
	if p.LisBox != nil {
		p.LisBox.ClientNum = p.LisBox.ClientNum + num
	}
	p.logger.Info("连接数", p.LisBox.ClientNum)
}
